<?php

namespace app\common\model\enneagram;

use app\common\model\Order;
use stdClass;
use think\Model;
use think\Exception;

/**
 * Results
 */
class Results extends Model
{
    // 表名
    protected $name = 'enneagram_results';
    const PAY_STATUS = [
        '0' => '未支付',
        '1' => '已支付',

    ];
    const PAY_STATUS_UNPAY = 0;
    const PAY_STATUS_PAID = 1;
    // 自动写入时间戳字段
    protected $autoWriteTimestamp = true;
    // 设置json类型字段
    protected $json = ['raw_scores', 'scores', 'selected_options', 'selected_types', 'report_data'];
    // 字段类型转换
    protected $type = [
        'raw_scores'       => 'json',
        'scores'           => 'json',
        'selected_options' => 'json',
        'selected_types'   => 'json',
        'report_data'      => 'json',
    ];


    // public function getRawScoresAttr($value)
    // {
    //     return json_decode($value ?: new stdClass);
    // }

    // public function getScoresAttr($value)
    // {
    //     return json_decode($value ?: new stdClass);
    // }

    // public function getSelectedOptionsAttr($value)
    // {
    //     return json_decode($value ?: new stdClass);
    // }

    // public function getSelectedTypesAttr($value)
    // {
    //     return json_decode($value ?: new stdClass);
    // }

    // public function getReportDataAttr($value)
    // {
    //     return json_decode($value ?: new stdClass);
    // }

    //--------------------------------------------------
    // public function setReportDataAttr($value)
    // {
    //     return json_encode($value ?: new stdClass);
    // }

    // public function setRawScoresAttr($value)
    // {
    //     return json_encode($value ?: new stdClass);
    // }

    // public function setScoresAttr($value)
    // {
    //     return json_encode($value ?: new stdClass);
    // }

    // public function setSelectedOptionsAttr($value)
    // {
    //     return json_encode($value ?: new stdClass);
    // }

    // public function setSelectedTypesAttr($value)
    // {
    //     return json_encode($value ?: new stdClass);
    // }

    public function user(): \think\model\relation\BelongsTo
    {
        return $this->belongsTo(\app\admin\model\User::class, 'user_id', 'id');
    }
    public function order(): \think\model\relation\BelongsTo
    {
        return $this->belongsTo(Order::class, 'result_id', 'id');
    }

    /**
     * 创建新测试
     * 
     * @param int $userId 用户ID
     * @return Test
     */
    public static function createTest(int $userId = 0): Results
    {
        $result = new self();
        $result->user_id = $userId;
        $result->create_time = time();
        $result->isFinish = 0; // 进行中
        $result->raw_scores = new stdClass;
        $result->scores = new stdClass;
        $result->selected_options = new stdClass;
        $result->selected_types = new stdClass;
        $result->report_data = new stdClass;
        $result->main_type = 0;
        $result->wing_type = 0;
        $result->save();
        return $result;
    }


    /**
     * 根据题目字段将题目数据进行分组
     * @param array $questions 题目数据
     * @return array 分组后的数据   
     */
    public static function groupQuestionsByField(array $questions, $groupByField = 'id'): array
    {
        $groupedData = [];
        foreach ($questions as $question) {
            $groupByKey = $question[$groupByField]; // 假设数组中有type字段
            if (!isset($groupedData[$groupByKey])) {
                $groupedData[$groupByKey] = $question;
            }
        }
        return $groupedData;
        // return array_reduce($questions, function ($carry, $item) use ($groupByField) {
        //     $groupByKey = $item[$groupByField]; // 假设数组中有type字段
        //     if (!isset($carry[$groupByKey])) {
        //         $carry[$groupByKey] = [];
        //     }
        //     $carry[$groupByKey][] = $item;
        //     return $carry;
        // }, []);
    }

    public static function initScoreData(): stdClass
    {
        $initScore = new stdClass;
        foreach (range(1, 9) as $number) {
            $initScore->$number = 0;
        }
        return $initScore;
    }

    /**
     * 计算结果
     * @param Results $result 结果对象
     * @return Results
     */
    public static function calculateThresholdResult(Results $result): Results
    {
        // 检查是否已经计算过
        if (is_object($result->report_data) && isset($result->report_data->scores) && isset($result->report_data->main_type)) {
            return $result;
        }

        // 数据验证
        if (!is_object($result->selected_options) || empty((array)$result->selected_options)) {
            throw new Exception('未答题无法生成报告！');
        }

        $questions = Questions::field('id,option_a_type,option_a_weight,option_b_type,option_b_weight')->group('id')->select()?->toArray();

        $groupedQuestions = self::groupQuestionsByField($questions, 'id');
        $selected_options = $result->selected_options;
        $selected_types = new stdClass;
        $raw_scores = self::initScoreData();
        $scores = self::initScoreData();

        // 计算原始分数 - 添加字段验证
        foreach ($selected_options as $qid => $selectedOption) {
            $question = $groupedQuestions[$qid] ?? null;
            if (!$question || !isset($question['id'])) {
                throw new Exception("题目ID {$qid} 不存在");
            }

            $selectedOption = strtolower($selectedOption);
            if (!in_array($selectedOption, ['a', 'b'])) {
                throw new Exception("无效的选项值: {$selectedOption}");
            }

            $selectedOptionKey = 'option_' . $selectedOption . '_type';
            $weightKey = 'option_' . $selectedOption . '_weight';

            if (!isset($question[$selectedOptionKey]) || !isset($question[$weightKey])) {
                throw new Exception("题目ID {$qid} 缺少必要字段");
            }

            $selectedType = $question[$selectedOptionKey];
            $selectedWeight = $question[$weightKey];

            // 验证类型是否在1-9范围内
            if ($selectedType < 1 || $selectedType > 9) {
                throw new Exception("无效的人格类型: {$selectedType}");
            }

            $selected_types->$qid = $selectedType;
            $raw_scores->$selectedType += ($selectedWeight && $selectedWeight > 0 ? 1 * $selectedWeight : 1);
        }
        // 计算化一百分数 - 以最高分化一百算法
        $maxRawScore = 0;
        // 找出原始分数中的最高分
        foreach ($raw_scores as $rawScore) {
            $maxRawScore = max($maxRawScore, $rawScore);
        }

        // 处理零分情况
        if ($maxRawScore <= 0) {
            throw new Exception('九型人格测试所有类型得分为0，可能存在数据问题');
        }

        // 归一化得分 - 将最高分转换为100分
        if ($maxRawScore > 0) {
            foreach ($raw_scores as $rtype => $score1) {
                $scores->$rtype = round(($score1 / $maxRawScore) * 100, 2);
            }
        }

        // 找出主要和次要人格类型 
        $mainType = $secondType = 0;
        $maxScore = $secondMaxScore = 0;
        $tiedTypes = [];
        // echo $secondType.PHP_EOL;
        // echo $secondMaxScore.PHP_EOL;
        foreach ($scores as $stype => $score2) {
            if ($score2 > $maxScore) {
                $secondMaxScore = $maxScore;
                $secondType = $mainType;
                $maxScore = $score2;
                $mainType = $stype;  // 更新主类型
                $tiedTypes = [$stype]; // 重置平局列表
            } elseif ($score2 == $maxScore) {
                $tiedTypes[] = $stype; // 添加到平局列表
            } elseif ($score2 > $secondMaxScore) {
                $secondMaxScore = $score2;
                $secondType = $stype;  // 直接更新次类型
            }
        }

        // 处理平局情况 - 只有一个最高分时直接使用，多个时比较原始分数
        if (count($tiedTypes) == 1) {
            $mainType = $tiedTypes[0];
        } else {
            // 平局时比较原始分数
            $highestRawScore = 0;
            $tiedRawTypes = [];

            foreach ($tiedTypes as $ttype) {
                if ($raw_scores->$ttype > $highestRawScore) {
                    $highestRawScore = $raw_scores->$ttype;
                    $tiedRawTypes = [$ttype];
                } elseif ($raw_scores->$ttype == $highestRawScore) {
                    $tiedRawTypes[] = $ttype;
                }
            }

            // 确保$tiedRawTypes不为空，避免min()函数错误
            if (!empty($tiedRawTypes)) {
                $mainType = min($tiedRawTypes);
            } else {
                // 如果出现异常情况，选择第一个平局类型
                $mainType = $tiedTypes[0];
            }
        }

        // 计算主类型显著性 - 主类型得分与次高分类型的差距
        $significance = $maxScore - $secondMaxScore;
        $isSignificant = $significance >= 15; // 差距15%以上视为显著

        // 确定侧翼人格 - 主类型相邻的两个类型中得分较高的一个
        $possibleWings = [
            ($mainType == 1 ? 9 : $mainType - 1),
            ($mainType == 9 ? 1 : $mainType + 1)
        ];

        // 先计算非相邻类型的最高分
        $nonWingTypes = array_diff(range(1, 9), $possibleWings, [$mainType]);
        // echo (implode(',', $nonWingTypes).PHP_EOL);
        $highestNonWingScore = -1;
        foreach ($nonWingTypes as $ntype) {
            $highestNonWingScore = max($highestNonWingScore, $scores->$ntype);
        }

        $wingType = 0;
        $maxWingScore = -1; // 使用-1确保即使得分为0也能正确比较

        // 动态调整侧翼阈值
        // $wingThreshold = max($maxScore * 0.4, $highestNonWingScore * 1); //去掉1.1倍
        $wingThreshold = $maxScore * 0.4;

        // 直接比较两个可能翼型的得分，并确保高于非相邻类型
        foreach ($possibleWings as $wing) {
            // 只有当得分高于非相邻类型的最高分时才考虑作为侧翼
            if ($scores->$wing > $maxWingScore && $scores->$wing > $highestNonWingScore) {
                $maxWingScore = $scores->$wing;
                $wingType = $wing;
            } elseif ($scores->$wing == $maxWingScore && $scores->$wing > $highestNonWingScore) {
                // 得分相同时比较原始分数，如果原始分数也相同，选择编号较小的一个（确保结果稳定）
                if ($wingType == 0 || $raw_scores->$wing > $raw_scores->$wingType) {
                    $wingType = $wing;
                } elseif ($raw_scores->$wing == $raw_scores->$wingType && $wing < $wingType) {
                    $wingType = $wing;
                }
            }
        }
        // 处理次类型与侧翼类型的关系 - 添加次类型有效性检查
        if ($secondType >= 1 && $secondType <= 9 && in_array($secondType, $possibleWings)) {
            // 如果次类型是可能的侧翼类型且得分高于当前侧翼类型和非相邻类型
            if ($scores->$secondType > $maxWingScore * 1.2 && $scores->$secondType > $highestNonWingScore) {
                $wingType = $secondType;
                $maxWingScore = $scores->$secondType;
            }
            // 如果次类型得分接近主类型，可以调整侧翼判定
            elseif ($scores->$secondType > $maxScore * 0.7 && $scores->$secondType > $highestNonWingScore) {
                $wingType = $secondType;
                $maxWingScore = $scores->$secondType;
            }
        }

        // 如果翼型分数低于阈值，则认为没有明显翼型
        // echo $maxWingScore.PHP_EOL;
        // echo $wingThreshold.PHP_EOL;
        // echo $highestNonWingScore.PHP_EOL;
        // echo $wingType.PHP_EOL;
        // echo $maxScore.PHP_EOL;
        // echo $secondMaxScore.PHP_EOL;
        // echo $wingType.PHP_EOL;
        // echo $mainType.PHP_EOL;
        // echo $maxWingScore.PHP_EOL;
        // echo $highestNonWingScore.PHP_EOL;
        // echo $maxScore.PHP_EOL;
        // echo $secondMaxScore.PHP_EOL;
        // echo $secondType.PHP_EOL;
        // echo $wingThreshold.PHP_EOL;
        if ($maxWingScore < $wingThreshold) {
            $wingType = 0;
        }

        // 计算结果可信度 (0-100)
        $reliability = 0;
        // 主类型显著性贡献 (最多60分)
        $reliability += min(60, $significance * 4);
        // 答题完整度贡献 (最多30分)
        $questionCount = count($questions);
        $answeredCount = count((array)$selected_options);
        $completeness = $questionCount > 0 ? ($answeredCount / $questionCount) * 30 : 0;
        $reliability += $completeness;
        // 翼型明确性贡献 (最多10分)
        $wingContribution = $wingType > 0 ? min(10, ($maxWingScore / $maxScore) * 10) : 0;
        $reliability += $wingContribution;

        // 结果可信度等级
        $reliabilityLevel = 'low';
        if ($reliability >= 80) {
            $reliabilityLevel = 'high';
        } elseif ($reliability >= 60) {
            $reliabilityLevel = 'medium';
        }

        // 更新结果数据
        $result->raw_scores = $raw_scores;
        $result->scores = $scores;
        $result->selected_types = $selected_types;
        $result->main_type = $mainType;
        $result->wing_type = $wingType;
        $result->update_time = time();
        $result->isFinish = 1;
        $enneagramTypes = Types::whereIn('type', [$mainType, $wingType, $secondType])->field('id,type,name,description,strengths,suggestions,other')->select();
        $mainEnneagramTypes = $enneagramTypes->where('type', $mainType)?->first();
        $wingEnneagramTypes = $enneagramTypes->where('type', $wingType)?->first();
        $secondEnneagramTypes = $enneagramTypes->where('type', $secondType)?->first();
        // 将 description/strengths/suggestions 三字段按换行符随机选取最多10行并以数组存储
        $fieldsToRandomize = ['description', 'strengths', 'suggestions','other'];
        $applyRandomLines = function ($item) use ($fieldsToRandomize) {
            if (!$item) {
                return;
            }
            $tiaoshu=20;
            foreach ($fieldsToRandomize as $field) {
                $value = $item->$field ?? '';
                if (!is_string($value) || $value === '') {
                    $item->$field = [];
                    continue;
                }
                // 以任何换行符分割，过滤空行并裁剪
                $lines = preg_split('/\R/u', $value);
                $lines = array_values(array_filter(array_map(function ($line) {
                    return trim($line);
                }, $lines), function ($line) {
                    return $line !== '';
                }));
                if (empty($lines)) {
                    $item->$field = [];
                    continue;
                }
                // 随机选取最多10行
                if (count($lines) > $tiaoshu) {
                    $keys = array_rand($lines, $tiaoshu);
                    $keys = is_array($keys) ? $keys : [$keys];
                    $selected = [];
                    foreach ($keys as $k) {
                        $selected[] = $lines[$k];
                    }
                    $item->$field = $selected;
                } else {
                    shuffle($lines);
                    $item->$field = $lines;
                }
            }
        };
        $applyRandomLines($mainEnneagramTypes);
        $applyRandomLines($wingEnneagramTypes);
        $applyRandomLines($secondEnneagramTypes);
        // 生成报告数据
        $reportData = [
            'scores' => $scores,
            'raw_scores' => $raw_scores,
            'mainEnneagram' => $mainEnneagramTypes ?? new stdClass,
            'wingEnneagram' => $wingEnneagramTypes ?? new stdClass,
            'secondEnneagram' => $secondEnneagramTypes ?? new stdClass,
            'main_type' => (int)$mainType,
            'wing_type' => (int)$wingType, // 侧翼类型
            'second_type' => (int)$secondType, // 次高分类型
            'significance' => $significance, // 主类型与次高分类型的差距
            'is_significant' => $isSignificant, // 主类型显著性,差距15%以上视为显著
            'reliability' => round($reliability, 2), // 结果可信度值 
            'reliability_level' => $reliabilityLevel, // 结果可信度等级
            'generate_time' => date('Y-m-d H:i:s')
        ];
        $result->report_data = $reportData;
        $result->save();
        return $result;
    }

    public static function calculateResult(Results $result, int $userId): Results
    {
        $questions = Questions::field('id,option_a_type,option_a_weight,option_b_type,option_b_weight')->groupBy('id')->select()->toArray();
        $groupedQuestions = self::groupQuestionsByField($questions, 'id');
        $selected_options = $result->selected_options;
        $selected_types = new stdClass;
        // 初始化各类型得分
        $raw_scores = self::initScoreData();
        $scores =  self::initScoreData();
        if (!empty($selected_options)) {
            foreach ($selected_options as $qid => $selectedOption) {
                $question = $groupedQuestions[$qid] ?? [];
                if (empty($question) || !isset($question['id'])) {
                    throw new Exception('题目不存在');
                }
                // $question = $question[0]; // 获取第一个元素，因为groupBy后的结构是数组
                $selectedOptionKey = 'option_' . strtolower($selectedOption) . '_type';
                $weightKey = 'option_' . strtolower($selectedOption) . '_weight';
                $selectedType = $question[$selectedOptionKey];
                $selected_types->$qid = $selectedType;
                $selectedWeight = $question[$weightKey];
                $raw_scores->$selectedType += ($selectedWeight && $selectedWeight > 0 ? 1 * $selectedWeight : 1);
            }
        }

        // 计算化一百分数 - 以最高分化一百算法
        $maxRawScore = 0;
        // 找出原始分数中的最高分
        foreach ($raw_scores as $score) {
            $maxRawScore = max($maxRawScore, $score);
        }

        // 归一化得分 - 将最高分转换为100分
        if ($maxRawScore > 0) {
            foreach ($raw_scores as $type => $score) {
                $scores->$type = round(($score / $maxRawScore) * 100, 2);
            }
        }

        // 找出主要人格类型
        $mainType = 0;
        $maxScore = 0;
        $tiedTypes = [];

        // 找出所有得分最高的类型
        foreach ($scores as $type => $score) {
            if ($score > $maxScore) {
                $maxScore = $score;
                $tiedTypes = [$type]; // 重置平局列表
            } elseif ($score == $maxScore) {
                $tiedTypes[] = $type; // 添加到平局列表
            }
        }

        // 处理平局情况 - 只有一个最高分时直接使用，多个时比较原始分数
        if (count($tiedTypes) == 1) {
            $mainType = $tiedTypes[0];
        } else {
            // 平局时比较原始分数
            $highestRawScore = 0;
            foreach ($tiedTypes as $type) {
                if ($raw_scores->$type > $highestRawScore) {
                    $highestRawScore = $raw_scores->$type;
                    $mainType = $type;
                }
            }
        }

        // 确定侧翼人格 - 主类型相邻的两个类型中得分较高的一个
        $possibleWings = [
            ($mainType == 1 ? 9 : $mainType - 1),
            ($mainType == 9 ? 1 : $mainType + 1)
        ];

        $wingType = 0;
        $maxWingScore = -1; // 使用-1确保即使得分为0也能正确比较

        // 直接比较两个可能翼型的得分
        foreach ($possibleWings as $wing) {
            if ($scores->$wing > $maxWingScore) {
                $maxWingScore = $scores->$wing;
                $wingType = $wing;
            } elseif ($scores->$wing == $maxWingScore && $raw_scores->$wing > $raw_scores->$wingType) {
                // 得分相同时比较原始分数
                $wingType = $wing;
            }
        }



        // 更新结果数据
        $result->raw_scores = $raw_scores;
        $result->scores = $scores;
        $result->selected_types = $selected_types;
        $result->main_type = $mainType;
        $result->wing_type = $wingType;
        $result->update_time = time();


        $enneagramTypes = Types::whereIn('type', [$mainType, $wingType])->field('id,type,name,description,strengths,suggestions,other')->select();
        $mainEnneagramTypes = $enneagramTypes->where('type', $mainType)?->toArray();
        $wingEnneagramTypes = $enneagramTypes->where('type', $wingType)?->toArray();
        // 生成报告数据
        $reportData = [
            'scores' => $scores,
            'raw_scores' => $raw_scores,
            'mainEnneagram' => $mainEnneagramTypes ?? [],
            'wingEnneagram' => $wingEnneagramTypes ?? [],
            'main_type' => $mainType,
            'wing_type' => $wingType, // 侧翼类型
            'generate_time' => date('Y-m-d H:i:s')
        ];
        $result->report_data = $reportData;
        $result->save();
        return $result;
    }
}
